/*
* WKB.GeometryFactory
* 
* initial implementation : 14.11.2008
*
* Copyright (c) Adam Sida, asida@centrum.cz.
* All rights reserved.
* 
* for license see Petra.Gis.License.txt
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.DataServices.WKB.Support;


namespace Petra.Gis.DataServices.WKB
{
  public partial class GeometryFactory : IGeometryFactory
  {
    //private pointers to functions to store in geometry mapping array ;-)
    private delegate GeometryBase CreateGeometryFn(BinaryReader mBReader/*, int arrayOffset*/, System.Type memberOf);
    private delegate byte[] SaveGeometryFn(GeometryBase geometry);

    private static Dictionary<WKBGeometryTypes, CreateGeometryFn> storageToGeometry;
    private static Dictionary<System.Type, SaveGeometryFn> geometryToStorage;


    static GeometryFactory()
    {

      //setup storage to geometry functions
      storageToGeometry = new Dictionary<WKBGeometryTypes, CreateGeometryFn>();
      storageToGeometry.Add(WKBGeometryTypes.Point, new CreateGeometryFn(binaryToPoint));
      storageToGeometry.Add(WKBGeometryTypes.LineString, new CreateGeometryFn(binaryToLinestring));
      storageToGeometry.Add(WKBGeometryTypes.Polygon, new CreateGeometryFn(binaryToPolygon));
      storageToGeometry.Add(WKBGeometryTypes.GeometryCollection, new CreateGeometryFn(binaryToGeometryCollection));
      storageToGeometry.Add(WKBGeometryTypes.MultiPoint, new CreateGeometryFn(binaryToGeometryCollection));
      storageToGeometry.Add(WKBGeometryTypes.MultiLineString, new CreateGeometryFn(binaryToGeometryCollection));
      storageToGeometry.Add(WKBGeometryTypes.MultiPolygon, new CreateGeometryFn(binaryToGeometryCollection));


      //setup geometry to storage functions
      geometryToStorage = new Dictionary<System.Type, SaveGeometryFn>();
      geometryToStorage.Add(typeof(Point), new SaveGeometryFn(pointToBinary));
      //geometryToStorage.Add(typeof(Label), new SaveGeometryFn(labelToBinary));
      geometryToStorage.Add(typeof(LineString), new SaveGeometryFn(lineStringToBinary));
      geometryToStorage.Add(typeof(Polygon), new SaveGeometryFn(polygonToBinary));
      //geometryToStorage.Add(typeof(Arc), new SaveGeometryFn(arcToBinary));
      geometryToStorage.Add(typeof(LineRing), new SaveGeometryFn(lineRingToBinary));

      geometryToStorage.Add(typeof(GeometryCollection), new SaveGeometryFn(geometryCollectionToBinary));
      geometryToStorage.Add(typeof(MultiPoint), new SaveGeometryFn(multiPointToBinary));
      //geometryToStorage.Add(typeof(MultiLabel), new SaveGeometryFn(multiLineToBinary));
      geometryToStorage.Add(typeof(MultiLine), new SaveGeometryFn(multiLineToBinary));
      geometryToStorage.Add(typeof(MultiArea), new SaveGeometryFn(multiAreaToBinary));

      //geometryToStorage.Add(typeof(Path), new SaveGeometryFn(pathToBinary));
      //geometryToStorage.Add(typeof(PathRing), new SaveGeometryFn(pathRingToBinary));
    }




    //-----------------------begin public functions block--------------------------

    public GeometryBase CreateGeometry(object vData)
    {
      //input must be byte[]
      byte[] b = (byte[])vData;

      WKBMemoryStream oIMStream = new WKBMemoryStream(b);
      BinaryReader mBReader = new BinaryReader(oIMStream);

      //set initial position in stream
      mBReader.BaseStream.Position = 0;

      return _createGeometry(mBReader/*, 0*/, null);
    }



    //interface function saveGeometry
    public object SaveGeometry(GeometryBase oGeom)
    {
      return (object)_saveGeometryBlob(oGeom);
    }



    //interface function canSave
    public bool CanSave(GeometryBase geometry)
    {
      //search Dictionary for delegate for this geometry
      try
      {
        SaveGeometryFn d = geometryToStorage[geometry.GetType()];
      }
      catch
      {
        return false;
      }
      return true;
    }



    //-----------------------end public functions block--------------------------




    /*
     internal implementation of createGeometry
     if this function is called from inside of geometry (Boundary, Collection, CompositePolygon, CompositePolyline)
     arrayOffset must point to begin of subgeometry
     and parentGeomType defines relations, WKB has different definition then SFS, must perform check and conversion !!
    */
    private static GeometryBase _createGeometry(BinaryReader mBReader/*, int arrayOffset*/, System.Type parentGeomType)
    {

      //mBReader.BaseStream.Position = arrayOffset;

      //test endian here
      //endian is always presented in geometry def
      WKB.ByteOrder dataEndian = (WKB.ByteOrder)mBReader.ReadByte();

      //get geometry type
      WKBGeometryTypes gType = (WKBGeometryTypes)EndianConverter.ToUInt32(mBReader.ReadUInt32(), dataEndian);

      //revert position to start of geometry
      //mBReader.BaseStream.Position = arrayOffset;
      mBReader.BaseStream.Position -= 5;

      //call function in dictionary that will create geometry from byte[]
      return storageToGeometry[gType](mBReader/*, arrayOffset*/, parentGeomType);

    }





    private static byte[] _saveGeometryBlob(GeometryBase geometry)
    {
      return geometryToStorage[geometry.GetType()](geometry);
    }


  }
}
